import express from 'express';
import cors from 'cors';
import fs from 'node:fs';
import crypto from 'node:crypto';

const app = express();

app.use(cors());

// 静态资源的缓存
// app.use(express.static('./static', {
//     maxAge: 1000 * 60,                  // 强缓存 cache-control 设置缓存时间
//     lastModified: true,                    // 开启协商缓存
//     // lastModified: new Date() + 1000 * 60 * 5,                    // 设置缓存时间
//     // etag: 'etag',                       // 设置缓存标识, 协商缓存的 etag方式
// }));

// 动态资源的缓存, 接口
app.get('/api', (req, res) => {
    // 设置缓存控制 强缓存 Expires (了解) ，它的判断机制是 本地时间对比 Expires 中设置的时间，有风险
    res.setHeader('Expires', new Date(Date.now() + 3600).toUTCString());  // 转成 UTC 时间
    res.send('Expires');
})
// cache-control
// 值的前缀  public：表示任何服务器都会缓存(包括代理服务器 如 CDN)
// 与之对应的是 private：表示只有客户端（浏览器）可以缓存
app.get('/api1', (req, res) => {
    // 设置缓存控制 强缓存 cache-control ， 它的值有很多
    res.setHeader('Cache-Control', 'public, max-age=60');   // max-age 设置有效时间60s, 这个是时长
    res.send('Cache-Control');
})
// 如果在一个接口中， Expires 和 Cache-Control 同时存在，Cache-Control 的 max-age 优先级高，缓存时间以后一个为准

// 读取文件修改时间，使用statSync方法的 mtime 属性
const fileModifyTime = () => fs.statSync('./index.js').mtime.toISOString();  // 获取文件的最后修改时间, 转成 ISO 格式

// 协商缓存 ，配套使用, 它给出的状态码是 304
// 如果强缓存和协商缓存同时出现，那么 只有强缓存生效，协商缓存不生效， 强缓存优先级高
// 如何解决这个问题？ 设置 Cache-Control: no-cache, 告诉浏览器，不要走强缓存，我要用协商缓存
// 与之相反的是， no-store, 告诉浏览器，不要走任何缓存，我要走网络请求, no-cache 和 no-store 这两个是互斥的
app.get('/api2', (req, res) => {
    // 设置缓存控制 协商缓存 Last-Modified 和 If-Modified-Since
    /*
    策略是： 第一次请求，服务返回会带 Last-Modified ， 第二次请求，浏览器就会带上 If-Modified-Since，它的值就是上一次服务端返回的Last-Modified的值，
    服务端会对比 If-Modified-Since 和 Last-Modified， 如果一致，就走缓存，不一致 浏览器会重新请求
    */
    res.setHeader('Cache-Control', 'no-cache');
    const ifModifiedSince = req.headers['if-modified-since'];
    const modifyTime = fileModifyTime()
    if (ifModifiedSince && ifModifiedSince === modifyTime) {
        console.log('缓存了');
        res.statusCode = 304;
        res.end();   // 结束请求， 浏览器不会走网络请求
        return;
    }
    console.log('没有缓存');
    res.setHeader('Last-Modified', modifyTime);    // 设置文件修改时间，带到头里，告诉浏览器
    res.send('Last-Modified');
})

const getFileHash = () => crypto.createHash('sha256').update(fs.readFileSync('./index.js')).digest('hex');

// Rtag跟last-modified 使用一样， 不过是跟 if-None-Match 搭配使用， 是对比文件的hash
app.get('/api3', (req, res) => {
    // 设置缓存控制 协商缓存 eTag 和 If-None-Match
    res.setHeader('Cache-Control', 'no-cache');
    const ifNodeMacth = req.headers['if-none-match'];
    const fileHash = getFileHash()
    if (ifNodeMacth && ifNodeMacth === fileHash) {
        console.log('缓存了');
        res.statusCode = 304;
        res.end();   // 结束请求， 浏览器不会走网络请求
        return;
    }
    console.log('没有缓存');
    res.setHeader('Etag', fileHash);    // 设置文件修改时间，带到头里，告诉浏览器
    res.send('ETag');
})



app.listen(3000, () => {
    console.log('Server is running on port 3000');
})